<?php
    namespace App\common;

    use App\Consts\ResponseConsts\ResponseStatusConsts;
    use App\Func\ClientFunc;
    use Illuminate\Http\Request;
    use App\Func\ResponseFunc;
    use App\common\cConfigurationCenter;
    use App\common\cRedis;
    use App\common\cFileSpliter;
    use App\common\cJson;
    use App\common\cDb;
	use Illuminate\Support\Facades\DB;
	use App\Models\File\FileModel;
	use App\Func\TokenFuc;
	use App\Consts\TableConsts\TableConsts;
	use App\Func\AuthFunc;
	use Illuminate\Http\Exceptions\HttpResponseException;
	use App\Service\Approval\PublicDingTalkApprovalService;
	use App\Consts\Approval\ApprovalSubmitTypeConsts;
	use App\Func\RequestFunc;
	use App\Func\ApprovalFunc;

    class cHelper  
	{
		# static singleton instance
		#
        private static   $m_o_single_instance; 

		/* submit review form to leader approval 
		 *
		 */
		public function gfSubmitReviewFormToLeaderApproval()
		{
			return	(
						new class ($this)
						{
							public function __construct($this_p)
							{
								$this->m_pb							= new PublicDingTalkApprovalService()	; # 实例化审批类
								$this->m_form						= []									; # 构建审批表单	
								$this->m_form_for_attachment		= null									;
								$this->m_file_ids					= ''									;
								$this->m_this_p						= $this_p								;
							}
							public function appendForm($form)
							{
								$this->m_form						= $form;
								return $this;
							}
							public function appendText($name,$value,callable $on_callable_append=null)
							{
								if(is_array($value))
								{
									return $this->appendMultipleText($name,$value,$on_callable_append);
								}
								else
								{
									$this->m_form []					= $this->m_pb->setTextTextAreaText($name,$value);

									#
									#
									if(is_null($on_callable_append)===false)
									{
										$on_callable_append($name,$value,$this);
									}

									return $this;
								}
							}
							private function appendMultipleText($name,$value,callable $on_callable_append=null)
							{
								$this->m_form []					= $this->m_pb->setTextTextAreaTextByArray($name,$value);

								#
								#
								if(is_null($on_callable_append)===false)
								{
									$on_callable_append($name,$value,$this);
								}
								
								return $this;
							}
							public function appendAttachmentFile($file_ids="924,925"/* test file id */)
							{
								$this->m_form_for_attachment	 	= $this->m_form;
								$this->m_form []					= $this->m_pb->setFiles('附件',$file_ids);
								$this->m_form_for_attachment[]		= ["name" => "附件","value" => $file_ids];
								$this->m_file_ids					= $this->m_file_ids . $file_ids;

								#
								#
								return $this;
							}
							public function appendDatetimeRange($name_begin,$name_end,$date_begin,$date_end,$on_callable_append=null)
							{
								$this->m_form []					= $this->m_pb->setStartDateEndDate([$name_begin,$name_end],[$date_begin,$date_end]);
								
								#
								#
								if(is_null($on_callable_append)===false)
								{
									$on_callable_append($name,$value,$this);
								}
								
								return $this;
							}
							public function appendTable($name,$value)
							{
								$form_talbe							= [];foreach($value as $one)
								{
									$form_table_row					= [];foreach($one as $k	=> $v)
									{
										$form_table_row []			= $this->m_pb->setTableValue($k,$v);
									}
									$form_talbe []					= $form_table_row;

									#
									#
									unset($form_table_row);
								}
								$this->m_form []					= $this->m_pb->setTable($name,$form_talbe);
								
								return $this;
							}

							/* start approval
							 *
							 */
							public function submitReview($approval_submit_type_const,$source_id,$remark='')
							{
								return	[
											"ding_talk_approval_id" => $this->m_pb->sendApproval	(
																										$approval_submit_type_const
																										,
																										$source_id
																										,
																										$this->m_form
																										,
																										[
																											'sqlComponent'	=> $this->m_form_for_attachment	,
																											'file_ids'		=> $this->m_file_ids			,
																											'remark'		=> $remark						,
																										]
																									)
										];
							}

							/* start reviewing by frontend WEB FORM   
							 * 
							 */
							public function submitReviewByFrontendWebForm ($approval_submit_type_const/* 丁丁审批表单id */,$source_id/* 丁丁审批表单外带数据,服务器回调会携带返回此数据 */,$frontend_web_form_array_data/* 前端构建的丁丁表单 */,&$ok=false)
							{
								try
								{
									# DD ok
									#
									$r									= ApprovalFunc::getDingTalkApprovalByWeb($approval_submit_type_const,$source_id,$frontend_web_form_array_data);
									$ok									= true;
									return $r;
								}
								catch(\Exception $exception)
								{
									# failed
									#
									return app()->gcHelper->gfApiEchoExHttpException((@$exception->getResponse()->getContent() ? json_decode($exception->getResponse()->getContent(),true)['msg'] : ""));
								}
							}
						}
					);
		}

		/* get header parameter 
		 *
		 */
		public function gfGetValueFromHeader($k)
		{
			return RequestFunc::getHeaderParams($k);
		}

		/* debug log to local file
		 * 
		 */
        public  function gfDebugPrintToLocalFileByBreezeer($s,$kill=false,$fileAppend=true)
        { 
            return $this->gfDebugPrintToLocalFile($s,$kill,'breezeer',$fileAppend);
        }
        public  function gfDebugPrintToLocalFileByDefault($s,$kill=false,$fileAppend=true)
        { 
            return $this->gfDebugPrintToLocalFile($s,$kill,'default',$fileAppend);
        }

		/* 校验用户的token是否有效
		 *
		 */
		public	function gfVerifyUserTokenOK($user_id,$user_token)
		{
			return  true===$this->toObjectEx(app()->gcProviderVerifyUserVisitPermission->verifyUserVisitPermissionOk($user_id,$user_token))->data->user_visit_permission?true:false;
		}

		private function gfVerifyFileContentHeaderFormatIsBOM($file_path)
		{
			$file_3_byte_header			= file_get_contents($file_path,false,null,0,3);
			$a							= ord(substr($file_3_byte_header,0,1));
			$b							= ord(substr($file_3_byte_header,1,1));
			$c							= ord(substr($file_3_byte_header,2,1));
			
			#
			#
			if($a==0xef && $b==0xbb && $c==0xbf)
			{
				return true	;
			}
			else 
			{
				return false;
			}
		}
		private function gfDebugPrintToLocalFile($s,$kill=false,$fileName=null,$fileAppend=true)
		{
			# local log file full path
			#
			$local_log_file_full_path	= realpath (dirname(__FILE__).'/../../').'/public/tmp.x/'.($fileName??'default').'.log.txt';

			#
			#
			if(!is_string($s))
			{
				$s						= print_r($s,true);
			}
			
			# append current date
			#
			$s							= '['.$this->gfGetCurrentLocalDate().'] '.$s.chr(13);

			# file all content will be truncated
			#
			if($fileAppend===true){}else{@unlink($local_log_file_full_path);}

            # append BOM flag
            #
            if(file_exists($local_log_file_full_path)===true){}else{$s= chr(0xEF).chr(0xBB).chr(0xBF).$s;}

			# write string to local file
			#
			file_put_contents ($local_log_file_full_path,$s,$fileAppend?FILE_APPEND:0);

			# kill process 
			#
			if($kill)
			{
				die;
			}
		}
		
		/* auto convert PHP number type variable to integer or float type 
		 *
		 */
		public function gfAutoConvertValueToNumberOrString($v)
		{
			if(is_numeric ($v)/* number */)
			{
				# contain '.' => tips: '123.000'
				#
				if(strpos($v,'.')===false)
				{
					return intval($v);		# int
				}
				else
				{
					return floatval($v);	# float
				}
			}
			else if(is_array($v)/* array */)
			{
				# recursion call myself
				#
				foreach($v as $kx => $vx)
				{
					$v[$kx] = $this->gfAutoConvertValueToNumberOrString($vx);/* */
				}

				return $v;
			}
			else
			{
				return ($v);
			}
		}
		public function gfIsNumeric($s)
		{
			return is_numeric($s);
		}
		public function gfIsPureInteger($s){
			
			if($s=='')
			{
				return false;
			}

			# $s in [0-9]
			#
			for($i=0;$i<strlen($s);$i++)
			{
				$one_char = substr($s,$i,1);if(in_array($one_char,[0,1,2,3,4,5,6,7,8,9]))
				{
				}
				else
				{
					return false;
				}
			}
			return true;			
		}
		public function gfGetcFile()
		{
			return ($this->m_cFile)();
		}
		public function gfFileSpliter()
		{
			return ($this->m_cFileSpliter)();
		}
		public function toObjectEx($ar)
		{
			# it is object
			#
			if(is_object($ar))
			{
				return $ar;
			}

			# it is array 
			#
			if(is_array($ar))
			{
				return json_decode($this->gfJsonEncode($ar));
			}
			else
			{
				return $this->toObject([]);
			}
		}
		public function toObject($ar)
		{
			return $this->gfConvertArrayToStdClass($ar);
		}
		public function toArray($o)
		{
			return get_object_vars($o);
		}
		public function gfConvertArrayToStdClass($ar)
		{
			return (object)$ar;
		}

		/* 获取后台登录的职员信息
		 * 
		 * 
		 *
		 */
		public function gfGetCurrentStaffInfo()
		{
			# try to get current login staff information
			#
			try
			{
				$ar					= TokenFuc::getCurrentStaffInfo()		;
			}
			catch(\Exception $ex)
			{
				$ar					= ['staff_id' => 0,'company_id' => 66]	;
			}

			# return $this->gfConvertArrayToStdClass($ar);
			#
			return $this->gfConvertArrayToStdClass($ar);
		}
		public function gfGetUploadFileInfo($arFileIds)
		{
			if(count($arFileIds)>0) 
			{
				return @(new FileModel())->getFilesByIds($arFileIds);
			}
		}
		public function gfGetTableNameByConstKey($const_key)
		{
			$rc			= new \ReflectionClass(\App\Consts\TableConsts\TableConsts::class);
			$const_key	= strtoupper($const_key);
			$tableName	= $rc->getConstants()[$const_key]??'';
			
			#
			#
			return $this->gfGetTableName($tableName);
		}
		public function gfGetTableName($tableName)
		{
			return DB::connection()->getTablePrefix().$tableName;
		}
        public function gfCreateUUID()
		{
            return md5(password_hash(hrtime(true).uniqid().intval(microtime(true)*1000*1000*1000), PASSWORD_DEFAULT));
        }
		public function gfGetIntValue($s) :int
		{
			if(!is_numeric($s))
			{
				return 0;				
			}

			return intval($s);
		}
		public function gfGetStringValue($s)
		{
			$s = htmlspecialchars ($s,ENT_QUOTES|ENT_HTML5);
			return $s;
		}

        public function zGfApiEcho($data,$msg,$statusC = 'ok',$code = 200)
		{
           return ResponseFunc::returnJson	(
												$statusC	
												,
												$data
												,
												$msg
												,
												$code
											);
        }
		public function gfGetRequestBodyValue($append_parameter=[])
		{
			# default empty json content
			#
			$empty_json_content	= '{}';
	        $method				= $_SERVER['REQUEST_METHOD'] ?? 'GET';if($method=='GET')
			{
				# get
				#
				$ar_all			= request()->all();if(count($ar_all)==0)
				{
					return $empty_json_content;
				}
				else
				{
					return app()->gcHelper->gfJsonEncode(request()->all());
				}
			}
			else
			{
				# post
				#
				$post_content	= request()->getContent();

				#
				#
				return $post_content==''?$empty_json_content:$post_content;
			}
		}

        public function gfDispatchCommandIdToClassMethod (&$thisx,$dispatch_method)
		{
            # invoke controller method ...
            #
            #
			{
				$rx                     = array();if($dispatch_method == ''){

					# error
					#
					$rx                 = $this->gfApiEchoReturn('',0,"method not found in class ".get_class($thisx)."");
				}else{
					
					# success
					#
					$rx                 = $thisx->$dispatch_method();
				}
			}

            # echo json to client
            #
            return $this->gfApiEcho($rx['data'],$rx['code'],$dispatch_method==''?'error':'ok',$rx['message']);
        }

		/* adjust date by year	
		 *
		 * tips : 
		 *						$nYear	----+			+--------------------- > 0 => delay
		 *									|			|
		 *									+----------->
		 *												|
		 *												+--------------------- < 0 => reduce 
		 *
		 *						$timeStamp	----------------------------------     => [ 2022-08-08 11:36:42 字符串格式的本地时间样式 ]
		 *						
		 *
		 */
        public function gfGetCurrentLocalDateAdjustByYear($timeStamp,$nYear=0)
		{
			$timesx = $timeStamp.' '.$nYear.' year';

			#
			#
			return $this->gfGetCurrentLocalDate(($timesx));
		}
		
		/* $timeStamp --+							  +----------> 1. 2022-08-08 11:36:42  => 字符串格式的本地时间样式 
		 *				|							 /
		 *				+---------------------------<
		 *											 \
		 *											  +----------> 2. 1542849318			=> unix 时间戳
		 *
		 *
		 */
        public function gfGetCurrentLocalDate($timeStamp=''/* */,$time_zone='Asia/Shanghai',$format=false)
		{
			# default timezone
			#
            date_default_timezone_set($time_zone);if($timeStamp!='')
			{
				if($this->gfIsPureInteger($timeStamp)/* unix 时间戳 */)
				{
				}
				else
				{
					$timeStamp = strtotime($timeStamp);
				}
			}
			else
			{
				$timeStamp		= 0;
			}

			# convert timestamp to local time format
			#
			$date_ex			= ($timeStamp>0)?date("Y-m-d H:i:s",$timeStamp):date("Y-m-d H:i:s");

			# format time show style
			#
			if($format===false)
			{
				return $date_ex;
			}
			else
			{
				$time_stamp_ex = strtotime($date_ex);

				#
				#
				return $this->toObjectEx	(
												[
													'ymd'		=> date("Y-m-d"			,$time_stamp_ex), 
													'ymdhi'		=> date("Y-m-d H:i"		,$time_stamp_ex), 
													'ymdhis'	=> date("Y-m-d H:i:s"	,$time_stamp_ex), 
												]
											);
			}
        }

		/* show php function call trace 
		 *
		 */
        public function gfDebugPrintCallFunctionTraceAndKillProcess()
		{
			debug_print_backtrace();die;
		}
        public function gfApiEchoReturnEx(&$ar)
		{
            return $this->gfApiEchoReturn($ar['data'],$ar['code'],$ar['message']);
        }
        public function gfApiEchoReturn($data='',$code=0,$message='')
		{
            return  array   (
                                'data'      => $data    ,
                                'code'      => $code    ,
                                'message'   => $message ,
                            );
        }
        public function gfJsonEncode ($ar)
		{
            return json_encode($ar, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }
        public function gfJsonDecode ($s,$callback_ok=null,$callback_error=null,$debug=false)
		{   
            return ($this->m_cJson)()->decode($s,$callback_ok,$callback_error,$debug);
        }
		public function gfIsEmptyString($s)
		{
			if($s==null)
			{
				return true;
			}
			if(is_string($s))
			{
				if(strtolower($s)=='null'||trim($s)=='')
				{
					return true;
				}
			}

			#
			#
			return false;
		}
		public function gfIsObjectEmpty($o)
		{
			return (count((array)$o)==0)?true:false;
		}
		public function gfApiEchoExHttpExceptionByCode401($s,$debug_flag_text='')
		{
			return $this->gfApiEchoExHttpException($s,$debug_flag_text,ResponseStatusConsts::UNAUTHORIZED_401_RESPONSE_STATUS);
		}
		public function gfApiEchoExHttpException($s,$debug_flag_text='',$responseStatusCode=ResponseStatusConsts::OK_200_RESPONSE_STATUS)
		{
			$b_debug_mode = env('APP_DEBUG');throw new HttpResponseException
			(
				# default error message
				#
				ResponseFunc::returnJson	(
												'error'																			,
												null																			,
												$s.(($b_debug_mode==false||$debug_flag_text=='')?'':'.'.$debug_flag_text)		,	# do not show debug flag text when in debug mode switch on
												# ResponseStatusConsts::OK_200_RESPONSE_STATUS									,
												$responseStatusCode
											)
			); 
		}
		public function gfDebugPrint($v,$type=false,$kill=true)
		{
			if($type)
			{
				var_dump($v); 
			}
			else
			{
				print_r($v);
			}

			# kill process 
			#
			if($kill){die;}
		}
        public function gfApiEchoEx_error ($ar)
		{
			return $this->gfApiEchoEx($ar,'error');
		}
        public function gfApiEchoEx_ok ($ar)
		{
			return $this->gfApiEchoEx($ar);
		}
        public function gfApiEchoEx  ($ar,$ok_or_error='ok')
		{
			return $this->gfApiEcho($ar['data'],$ar['code'],$ok_or_error??'ok',$ar['message']);        
		}
        private function gfApiEcho ($data /* data */,$code=0/* code */,$ok_or_error='ok' /* status => ok or error */,$message=''/* tips: message for status */ ) 
		{   
			$data							= @is_array($data[0])?$data[0]:$data					;
			$data['code.extension.flag']	= $code													;

			#
			#
            return ResponseFunc::returnJson		(
                                                    $ok_or_error                                    ,
													$data											,
                                                    $message                                        ,
                                                    ResponseStatusConsts::OK_200_RESPONSE_STATUS    ,
                                                );
        }

        public function gfDicData()
		{
            return ($this->m_cDataF)();
        }
		public function getOneSubNodeDateByParentkeyAndSubNodeValue($parent_key,$sub_value)
		{
			foreach($this->getAllSubNodeDataByParentkey($parent_key) as $one)
			{
				$one_ooo = $this->toObjectEx($one);if($one_ooo->dictionary_value==$sub_value)
				{
					return $one_ooo;
				}
			}
			return null;
		}
		public function getAllSubNodeDataByParentkey($parent_key)
		{
            return $this->gfDicData()->getAllSubNodeDataByParentkey($parent_key);
		}

		public function getDictionaryInfoByDictionaryKeyEx($key)
		{
			return (object)$this->getDictionaryInfoByDictionaryKey($key);
		}
        public function getDictionaryNodeByDictionKey($key,$select = ["*"])
		{
            return $this->gfDicData()->getDictionaryNodeByDictionKey($key,$select);
        }

        public function getDictionaryInfoByDictionaryKey($key,$select = ["*"])
		{
            return $this->gfDicData()->getDictionaryInfoByDictionaryKey($key,$select);
        }

		/* get system cofiguration center object
		 *
		 * by breezeer @ 2020.10.31
		 *
		 *
		 */  
		public function gfGetConfigurationCenter()
		{
			return ($this->m_cConfigurationCenter)();
		}

        /**
         * 校验当前值是否在父节点下存在
         * @param $parentDictionaryKey "父节点枚举"
         * @param $val "实际传递的值"
         * @param $msg "自定义错误提示"
         */
        public function checkerDictionaryHasNodesByParentKey($parentDictionaryKey,$val,$msg)
		{
            return $this->gfDicData()->checkerDictionaryHasNodesByParentKey($parentDictionaryKey,$val,$msg);
        }

        /**
         * 通过父节点key自动匹配子节点value值
         * @param $parentDictionaryKey
         * @param $val
         * @return mixed
         */
        public function getDictionaryMapValueByParentKey($parentDictionaryKey,$val)
		{
            return $this->gfDicData()->getDictionaryMapValueByParentKey($parentDictionaryKey,$val);
        }

        /**
         * 通过父节点key自动匹配子节点审批value值
         * @param $parentDictionaryKey
         * @param $val
         * @return mixed
         */
        public function getDictionaryMapAuditValueByParentKey($parentDictionaryKey,$val)
		{
            return $this->gfDicData()->getDictionaryMapAuditValueByParentKey($parentDictionaryKey,$val);
        }



		/* query data from multi different database with the same SQL
		 *
		 *
		 *
		 *
		 *
		 */
		public function gfDbQueryInMultiDB($sql)
		{
			# query
			#
			#
			$ar		= [];foreach((new cConfigurationCenter())->get_all_company_mysql_connection_string() as $k => $v)
			{
				$rs	= DB::connection($k)->select($sql);foreach($rs as $v)
				{
					$v->mysql_connection_flag	= $k;	# mysql connection string flag
					$ar []						= $v;	# one row result
				}
			}

			# callback result to client
			#
			#
			return	(	new class($ar)
						{
							public function __construct (&$ar)
							{
								$this->m_ar		= $ar;
							}
							public function forEachRow	($callback_for_each=null)
							{
								if($callback_for_each==null)
								{
									return $this->m_ar;
								}

								#
								#
								foreach ($this->m_ar as $k => $v)
								{
									$callback_for_each ($v);
								}
							}
						}
					);
		}
        public function gfDbQuery()
		{
            return ($this->m_cDb)();
        }
        public function gfGetRedis():iCache
		{
            return ($this->m_iCache)();
        }

        # lazy dynamic inject dependency  
        #
        #
        public function __set($name,$value)
		{
            if(property_exists($this,$name))
			{
			}
			else
			{
                $this->$name = $value;
            }
            return $this;
        }


		public function echo_to_console($s)
		{
			echo $s;
		}
		public function echo_to_console_with_ln($s)
		{
			$this->echo_to_console($s);
			echo "\n";
		}
		public function echo_to_console_with_format($format,... $vx)
		{
			echo sprintf($format,... $vx);
		}
		public function console()
		{
			return new class ($this)
			{
				public function __construct ($p_this)
				{
					$this->m_p_this	= $p_this	;
				}
				public function read($read_tips=null,$on_callback_for_yes=null,$on_callback_for_no=null)
				{
					if($read_tips!=null)
					{
						$this->m_p_this->echo_to_console($read_tips);
					}
					$s = trim(fgets(STDIN));

					#
					#
					#
					$on_callback_for_yes != null && in_array($s,['yes'	,'y'])?$on_callback_for_yes()	: '';
					$on_callback_for_no	 != null && in_array($s,['no'	,'n'])?$on_callback_for_no()	: '';

					#
					#
					#
					return $s;
				}
			};
		}

     
        # object factory
        #     
        #
        public static function createHelper ()
		{
            if(cHelper::$m_o_single_instance==null)
			{
				#
				#
                cHelper::$m_o_single_instance							= new cHelper									()	;

				#
				#
                cHelper::$m_o_single_instance->m_cConfigurationCenter	= function(){return new cConfigurationCenter	()	;};
                cHelper::$m_o_single_instance->m_cJson					= function(){return new cJson					()	;};
                cHelper::$m_o_single_instance->m_cDb					= function(){return new cDB						()	;};
                cHelper::$m_o_single_instance->m_iCache					= function(){return new cRedis					()	;};
                cHelper::$m_o_single_instance->m_cFile					= function(){return new cFile					()	;};
                cHelper::$m_o_single_instance->m_cDataF					= function(){return new cDataF					()	;};
                cHelper::$m_o_single_instance->m_cDealyDateF			= function(){return new cDelayDateF				()	;};
                cHelper::$m_o_single_instance->m_cFileSpliter			= function(){return new cFileSpliter			()	;};
            }

			#
			#
            return cHelper::$m_o_single_instance;
        }
    }
